home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Diamond Collection
/
The Diamond Collection (Software Vault)(Digital Impact).ISO
/
cdr05
/
ppp25.zip
/
PPP.DOC
next >
Wrap
Text File
|
1994-11-14
|
77KB
|
2,087 lines
POV-Ray and
Polyray
Preprocessor Version 2.5
By John R. Wind
November 1994
Compuserve: 72604,1344
Internet: JOHNWIND@DELPHI.COM
Introduction:
It started when I saw several utilities for POV that would do simple
things like a rectangular array of objects, or a circular array of
objects. I thought that it would be nice if POV or Polyray had a
command like a For-Next loop to do things like this. After working on
this program (referred to as P-cubed), I see that if the raytracer had
commands like that, the only indication you would have if there was
some error in your source file is that visually the scene might not
look right. It would be better to see a listing of the the kind of
objects that a command like that produced before it was rendered. The
answer is a preprocessor that can parse a file with these additional
commands embedded along with raytracer statements and output a normal
raytracer source file that you can render. Well, I fired up QBASIC and
hacked away. Why QBASIC? Because it was there, and I understand BASIC
better than C language. Plus, I could always compile the results with
Microsoft QuickBasic 4.5 for a stand-alone command line executable. I
know that C language may produce a faster executable program, but when
you consider that raytracing may take several minutes even on a fast
machine, 1 minute to execute a preprocessor is a drop in the bucket. As
time went on, one feature led to another, and the result grew from humble
beginnings into a robust language.
Current features include:
Floating Point, and Vector variables
Extensive Vector and Scalar Math Functions.
Loops (DO, UNTIL)
Conditional Statements (IF, ELSE)
Recursive Subprograms
Local and World Axis Calculations
Can do L-system objects similar to LPARSER
Multiple Input and Output files
Will work with POV or Polyray
Usage:
PPP Sourcefilename[.ppp] [Outputfilename]
The Output file is optional. If one is not specified then the
default output will be sent to a temporary default output file
"temp.$$$" which is deleted at the end of processing. This
allows one to specify output file(s) from within the source file
if desired.
Conventions: Some things to take note of
* P-cubed is designed as a preprocessor. It passes raytracer
statements in the input file straight through to the output file.
That is, an ordinary POV or Polyray input file will not be changed by
running it through P-cubed. P-cubed will take a POV or Polyray input
file with P-cubed commands added and use those commands to generate a
modified output file in POV or Polyray format that can be rendered.
* P-cubed is not limited to POV or Polyray format, it has just been
designed not to let any P-cubed commands be confused with POV or
Polyray statements, and it outputs vectors enclosed in <> brackets
that are required by POV and Polyray (i.e. <1, 2, 3>). It is also
capable of generating Raw triangle files, Batch files, Text files in
general, use your imagination.
* Lines that do not contain a P-cubed command as the first word are
passed on to the output file as is. The only exception is when a line
contains embedded value references. Then the line is passed on to
the output file with the value(s) inserted in place of the reference(s).
This is much the same as DOS batch files make use of environment variables.
* All P-cubed commands begin with a # symbol i.e. #var, #print,
but raytracer statements that already begin with a # symbol like
"#include" and "#declare" will be passed on to the output file and not
confused with a P-cubed command.
* Blank lines will be passed on to the output file. If you need
some blank lines in the source file, but don't want them to show up in
the output file, then put a single # symbol at the beginning of a blank
line in the source file and that line will not be passed on to the
output file.
* P-cubed does very little syntax checking. I've added a few
error messages for obvious errors, but I didn't want to waste all my
time playing "what if". If you don't get an error message, but the
results just aren't coming out right, output some of the variables at
intermediate points in the program to let you trace what is happening
(or not happening). This will help you locate the error.
* I've tried to make P-cubed syntax free format and as tolerant as
possible of minor syntax variations.
Example: "#let x = 2 2 +" will parse just the same as "#let x=2 2 +".
* Variables can be Floating point numbers or Vectors and the math
functions don't confirm that you are using mathematically well defined
combinations. Example: You can multiply a vector by a vector.
This is not defined mathematically, and P-cubed will produce equally
meaningless results, but it will do it.
* All numbers are kept track of internally as double-precision 16
digit floating point values, but are rounded to a user specified number
of digits when they are output to a file. This helps offset some
problems with the precision of floating point numbers and raytracers
have problems with double-precision numbers in QBASIC exponential
format i.e. 1D-6.
Now, on to programming.
Variables:
Variables may represent Floating point, or Vector values. P-cubed
keeps track of the variable type for math operations and output
formatting. All variables must begin with an alphabetic character
(a-z), and should not be the same as one of the built-in function
names. Variables may be up 12 characters long and are case insensitive.
Variables are declared by using the #var command.
The syntax for the #var command is:
#var name1[, name2, name3, ...]
Examples: #var john
#var a, b, cat, dog
The #var command will define the variable(s) and initialize the
value(s) of the variable(s) to a Floating point 0.
Variables may be defined multiple times.
Example:
--> #var a, b, c, b, d, [b:6]
In the example we defined a, b, c, then defined b again, then
defined d, and defined b six more times. This is kind of a poor man's
one-dimensional array. The specification "[varname:number]" defines
a quantity of variables of name=varname and amount=number. The same
specifcation "[varname:index]" also lets us refer to previous values
of a variable that has been defined multiple times. When we refer to
these multiple variables, "b" would refer to the very latest definition
of b (in our case the last element of the [b:6] array). Likewise, [b:0]
also refers to the very latest definition of the "b" variable. [b:1]
would refer to the next-to-the-last definition of "b". [b:5] would refer
to the first instance of "b" right after the definition of "d", [b:6]
would refer to the definition of "b" that is between c and d, and [b:7]
would refer to the first definition of "b" right after "a".
Outputting a line to the output file:
An output line is simply any line that does not begin with a P-cubed
command. Even blank lines will be sent to the output file. You can
embed data in an output line by enclosing it in percent signs.
Example: (Assuming variable a = 3.45 and b={3, 4, 5} )
sphere { %b%, %a% }
Would output:
sphere { < 3, 4, 5 >, 3.45 }
In our example, "a" was a variable of value 3.45 and "b" was a variable
that had the vector value {3, 4, 5}. We put the specification %a% in
our output line where we wanted the value of "a" to be and %b% where we
wanted the value of "b" to be. Note that vectors come out enclosed in
< > brackets consistent with POV and Polyray vector definitions. If a
% sign is required in the output, then use "%%" in the input file to
specify a percent sign.
Example:
I am 100%% ready.
Would output as:
I am 100% ready.
Anything that evaluates as a number or a vector may be put in an
output line embedded between % signs. This includes a number, a
vector, a variable, and an evaluated expression.
Appending to an Output line:
To append to a previous output line, just begin the next line with
"#+" and everything immediately after the "#+" will be added to the
previous output line.
Example:
abcdefg
#+hijklmnop
Would output as:
abcdefghijklmnop
Comments:
To add a comment to the source file only, so that it does not show
up in the output file, Begin a line with the # symbol and then at
least one space.
Example:
# This is a comment
# This is a comment also
#This is not a comment and will be in the output file
The first two lines would not show up in the output file, but the
third one would because there was not at least one space between the #
symbol and the first word. Completely blank lines will be passed on
to the output file, so, if you want to add some space to your source
file and not the output, remember to Put a # symbol at the beginning
of a blank line and it will not show up in the output.
The #print command:
The #print command serves to send output to the screen only.
Nothing that is on the same line with the print command is sent to the
output file, just to the screen. The syntax is:
#print line_output_to_screen
Example:
#var a, b
#let a = 1
#let b = { 3, 4, 5 }
#print a=%a%, and b=%b%
Would print to the screen only:
a= 1, and b= < 3, 4, 5 >
This same line without the #print command in front would be sent to
the output file.
The #open command:
The #open command lets you open a file for input or output. Any
number of files may be open at one time up to the limit set by the
FILES statement in your CONFIG.SYS file.
The syntax is:
#open in|out variable filename
Example:
#var inpnum, outnum
#open in inpnum infile.ppp
#open out outnum outfile.pov
The example would open a file called "infile.ppp" for input and it
will be referred to by the file number in variable "inpnum". The
example also opens a file called "outfile.pov" for output and it will
be referred to by the file number in variable "outnum". Values may
be embedded in the filname in order to add numbers to a filename.
These numbers are truncated to a positive integer value.
Example:
#var outnum, num
#let num = 99.1
#open out outnum file%num%.ppp
The example would open a file called "file99.ppp" for output and it
will be referred to by the file number in variable "outnum".
The #input command:
The #input command directs P-cubed to get it's input from another
source file that you have opened. The new source file is parsed until
it's end is reached, then P-cubed returns to parsing the previous
source file. You may "#input" the same source file several times if
desired (useful for multiple copies of an object in another source
file). The syntax is:
#input var
where the variable contains the file number of an open file.
Example:
#var inpnum
#open in inpnum myfile.ppp
#input inpnum
The example would open the file "myfile.ppp" for input and then parse
the commands in that file. When the end of "myfile.ppp" is reached,
P-cubed will return to parsing the next statement of the previous
input file.
The #output command:
The #output command directs P-cubed to send it's output to another
output file that you have opened. The #output command can also be used
to send P-cubed output back to the original default output file.
The syntax is:
#output [variable]
The optional variable contains the file number of an open file.
Example:
#var outnum
#open out outnum myfile.pov
#output outnum
line sent to myfile.pov
#output
line sent to default output file
The example would open a file named "myfile.pov" for output and then
tells P-cubed to send all output to that file. After one line is sent
to "myfile.pov", the second #output command tells P-cubed to send all
output to the default output file again.
The #close command:
The #close command closes a file that you have opened for input or
output. This is usually not necessary as P-cubed closes all files
when it is finished executing, but you may run into situations when
there may be too many files open at once and you have to close a few.
The syntax is:
#close variable
where the variable contains the file number of an open file.
The #let command:
The #let command serves to assign a value to a variable. This value
could be a literal floating point number, a literal vector, the value of
another variable, or an evaluated mathematical expression.
The syntax for a #let command is:
#let variable = value
Examples:
#let a = 1
#let b = { 1, 2, 3 }
#let d = a
#let [b:2] = 5
#let e = a c +
#let f = e rand
Let's start with the simplest case:
#let var = value
A value may be a literal floating point number i.e. 3.1415926
A value may also be a literal vector i.e. { 1, 2, 3.1415926 }.
Vectors are three values sepatated by commas and enclosed in { }
brackets.
Example:
#let a = 4.25 <-- "a" is a number
#let b = {1.2, 2.3, 3.4} <-- "b" is a vector
Values may also be represented by a variable reference.
Example:
#let a = b
means put the value of variable "b" in variable "a"
Now notice that a literal vector contains three values, which may also
be represented by a variable reference.
Example:
#let b = 1
#let c = { b, 2, 3 }
means that the value of variable "c" is the vector { 1, 2, 3 }
A value may also be preceeded by a "-" to negate it
Example:
#let b = -1
#let c = 2
#let d = { b, -c, 3 }
means that the value of variable "d" is the vector { -1, -2, 3 }
Now let's try some math.
First, all math is done in "reverse polish notation", referred to as
"RPN". That is, we switch from having the math operator between the
two arguments (infix) to putting it at the end (postfix).
Example:
#let a = 2 + 2 <-- Algebraic Notation
Would be written:
#let a = 2 2 + <-- RPN
This may seem like a trivial change, but it makes it much easier for
the program to understand complex expressions.
Example:
#let a = (4 + 3) * (5 + 6) <-- Algebraic Notation
Would be written as:
#let a = 4 3 + 5 6 + * <-- RPN
In algebraic notation, the program would have to keep track of
precedence of operators. That is why we had to add the parentheses,
so that the additions were performed before the multiplication.
Reverse polish notation (RPN) gets away from this by simply
evaluating the expression from left to right. Reading from left to
right, the RPN sequence of the last example goes like this:
Step: Reads: Function: Stack:
1. 4 Push 4 onto the stack 4 <-
2. 3 Push 3 onto the stack 4 3 <-
3. + Remove the top two numbers
from the stack (4 and 3)
and add them, and put the
answer on the stack. 7 <-
5. 5 Push 5 onto the stack 7 5 <-
6. 6 Push 6 onto the stack 7 5 6 <-
7. + Remove the top two numbers
from the stack (5 and 6)
and add them, and put the
answer on the stack. 7 11 <-
8. * Remove the top two numbers
from the stack (the answers
from step 3 and step 7) and
multiply them and put
the answer on the stack 77 <-
9. The answer is removed from <-
the stack and put in the
variable "a"
The stack is a temporary storage place for numbers and vectors,
sometimes called a last-in-first-out stack because the last number you
put in is the first number you get out. All math functions just get
numbers from the top of the stack, perform some mathematical operation
on them, and then put the answer on the stack. The #let command
evaluates the expression, gets what is left on top the stack, and puts
it in the variable you specify. So "#let a = 4" is evaluating an
expression that puts 4 on top of the stack, and then puts the number on
top of the stack in variable "a". Actually, the correct description of
a #let command is:
#let variable = expression
P-cubed has many RPN math functions and they all make use of the
stack, so in order to describe the RPN math functions I will include a
stack effect description.
Example:
(n1 n2 -> n3)
Means that the function will expect values n1 and n2 to be on
top of the stack and it will remove them, perform some function on
them, and place the answer n3 on top of the stack. Any value placed
in an RPN expression is pushed onto the stack automatically, so if the
function expects two values from the stack, just place those two
values before the function in the RPN expression.
Example:
2 3 +
Would place the values 2 and 3 on the stack, perform the
addition function, and leave the value 5 on top of the stack.
Here are RPN Scalar and Vector math functions.
Function: Stack: Description:
+ (n1 n2 -> n3) Adds n1 and n2
+= (v1 n1 -> ) Adds n1 to variable v1 and puts the
answer directly in variable v1.
No value is left on the stack.
++ (v1 -> ) Adds 1 to variable v1 and puts the
answer directly in variable v1.
No value is left on the stack
- (n1 n2 -> n3) Subtracts n2 from n1
-= (v1 n1 -> ) Subtracts n1 from variable v1 and puts
the answer directly in variable v1.
No value is left in the stack.
-- (v1 -> ) Subtracts 1 from variable v1 and puts
the answer directly in variable v1.
No value is left on the stack.
* (n1 n2 -> n3) Multiplies n1 by n2
*= (v1 n1 -> ) Multiplies variable v1 by n1 and puts
the answer directly in variable v1.
No value is left on the stack.
/ (n1 n2 -> n3) Divides n1 by n2
/= (v1 n1 -> ) Divides variable v1 by n1 and puts the
answer directly in variable v1.
No value is left on the stack.
^ (n1 n2 -> n3) Raises n1 to the n2 power
abs (n1 -> n2) Finds the absolute value of n1
sqr (n1 -> n2) Finds the Square root of n1
sgn (n1 -> n2) -1 if n1 is negative,
0 if n1 is zero
1 if n1 is positive
log (n1 -> n2) Natural logarithim of n1
exp (n1 -> n2) e to the n1 power
sin (n1 -> n2) Sine of n1 (in degrees)
cos (n1 -> n2) Cosine of n1 (in degrees)
tan (n1 -> n2) Tangent of n1 (in degrees)
atn (n1 -> n2) ArcTangent of n1 (answer in degrees)
int (n1 -> n2) Largest integer less than or equal to n1
fix (n1 -> n2) n1 truncated to an integer value
Now, some RPN vector math
dot (n1 n2 -> n3) Dot product of vector n1 and n2
cross (n1 n2 -> n3) Cross product of vector n1 and n2
mag (n1 -> n2) Magnitude of vector n1
rot (n1 n2 -> n3) Vector n1 rotated about the origin by
the x, y, and z degrees in vector n2.
i.e. "{1, 0, 0} {30, 60, 90} rot" would
rotate the vector {1, 0, 0} by 30
degrees about the x-axis, then 60 degrees
about the y-axis, and then 90 degrees
about the z-axis. A left-handed
coordinate system is used.
turn (n1 n2 n3 -> n4) Vector n1 rotated about vector n2 by
n3 degrees.
i.e. "{1, 0, 0} {1, 1, 0} 180 turn" would
rotate the vector {1, 0, 0} by 180 degrees
about vector {1, 1, 0} in a left-handed
direction.
getx (n1 -> n2) Gets the x component of vector n1
gety (n1 -> n2) Gets the y component of vector n1
getz (n1 -> n2) Gets the z component of vector n1
getxyz (vec -> nz ny nx) Decomposes a vector into x, y, and z
components.
vector (nx ny nz -> vec) Takes three components, x, y, and z,
and generates a vector
and some misc. functions
rand (n1 -> n2) A random number from 0 to n1 if n1 is a
number, or from {0, 0, 0} to {x1, y1, z1}
if n1 is a vector.
noise (n1 -> n2) Finds a noise value from 0 to 1 for the
vector coordinate n1. Coordinates that
are more than one unit distant from
each other will have noise values that
are relatively random with respect to
each other, but coordinates that are
less than 1 unit distant from each
other will have noise values that are
almost the same. This provides a
smoothly varying random value for all
points in space.
== (v1 n1 -> ) Puts value n1 in variable v1.
No value is left on the stack.
dupe (n1 -> n1 n1) Duplicates n1 on top of the stack
swap (n1 n2 -> n2 n1) Swaps n1 and n2 in the stack
drop (n1 -> ) Drops the number on top of the stack
over (n1 n2 -> n1 n2 n1)
Duplicates n1 (the next-to-top value)
and puts it on top of the stack.
vstack? ( -> n1) Current depth of Value stack
astack? ( -> n1) Current depth of Axis stack
level? ( -> n1) Current nested and/or recursive subprg
call level.
An evaluated expression may be used in place of a value by enclosing
the expression in parenthesis. i.e. (2 2 +) may be substituted anywhere
you would put the value 4.
Example:
#let a = { 1, (2 3 *), 9}
Would set "a" equal to the vector {1, 6, 9}
or:
sphere { < 1, 0, 0 >, %(2 5 +)% }
Would output the line
sphere { < 1, 0, 0 >, 7 }
Perhaps, you already have a have a value on top of the stack and would
like to use it somewhere. The value on top of the stack may be be
specified by just "()".
Example: (Assuming the value 4 is on top of the stack)
#let a = { 1, 2, () }
Would set a = { 1, 2, 4 }
The #eval command:
The #eval command takes an expression and evaluates it. This is for
expressions that either have no effect on the stack, or have an effect
on the stack that will be made use of later.
Example:
#eval {0, 0, 0} 5 {20, 0, 0} 10 connect
Would find the cone section that would smoothly connect the
two spheres whose centers and radii are specified and leave
the answer on top of the stack in the form:
rad2 end2 rad1 end1 <-
So that later they may be popped off the stack in the order:
end1 rad1 end2 rad2
or, just used in a raytracer cone statement by
cone { %()%, %()%, %()%, %()% }
Where
() pops --> end1 rad1 end2 rad2
The #push and #pop commands:
The #push and #pop commands serve to save and recover values on the
"last in first out" stack. Their syntax is:
#push value [value2 value3 ...]
#pop var [var2 var3 ...]
Example:
#var a, b
#let a = 1
#let b = { 3, 4, 5 }
#push a b
#pop a b
#print a=%a%, b=%b%
Would print:
a= < 3, 4, 5 >, b= 1
The example demonstrates a simple use of the #push and #pop commands,
which swaps the values of two variables. Note that the values are
swapped because the value of "b" was the last pushed onto the stack
and the first value popped off of the stack was put in "a".
The #if, #else and #endif commands:
The #if, #else and #endif commands provide a way to process blocks of
commands only under certain conditions.
Their syntax is:
#if conditional_expression
...
#else [optional_expression]
...
#endif [optional_expression]
The #else command is optional. You can have just #if and #endif.
The conditional_expression after #if is any expression that leaves
either a zero or non-zero value on top of the stack. A zero value
means "false" and the #if section will be skipped and the #else
section will be processed. A non-zero value means "true" and the #if
section will be processed and the #else section skipped.
RPN functions for conditional expressions are:
Function: Stack: Description:
< (n1 n2 -> n3) n3 is true if n1 is less than n2
> (n1 n2 -> n3) n3 is true if n1 is greater than n2
= (n1 n2 -> n3) n3 is true if n1 equals n2
<> (n1 n2 -> n3) n3 is true if n1 doesn't equal n2
<= (n1 n2 -> n3) n3 is true if n1 is less than or equal to n2
>= (n1 n2 -> n3) n3 true if n1 is greater than or equal to n2
not (n1 -> n2) n2 is false if n1 is true and vice versa.
and (n1 n2 -> n3) n3 is true if n1 and n2 are true.
or (n1 n2 -> n3) n3 is true if n1 or n2 is true.
xor (n1 n2 -> n3) n3 is true if n1 or n2 is true, but not both.
Where "true" means n3 is a non-zero value, as opposed to "false"
which means that n3 would be zero.
Here is an example:
#if a 2 <
#print a is less than 2
#else
#print a is greater than or equal to 2
#endif
You may append an optional expression to the #else and #endif commands
if desired. This is just for some additional expressions that you
want to process and is not required.
The #do and #until commands:
The #do and #until commands let you add loops to your program.
The syntax is:
#do [optional_expression]
...
...
#until [conditional_expression]
The conditional expression after the #until command is optional and
will default to the false condition. That is, just #until by itself
will continue the loop indefinitely, or at least until a #break
instruction is encountered (see below). Otherwise, the loop continues
if the conditional expression after #until evaluates false, or the
loop exits if the conditional expression after #until evaluates true.
Example:
#do a 1 ==
#print a=%a%
#until a ++ a 3 >
Would print:
a= 1
a= 2
a= 3
The expression after #do initialized "a" to 1. As the loop iterated,
the #until command incremented "a" and compared it to 3, and finally
exitted when "a" was greater than 3.
The #break command:
The #break command will break out of a loop immediately.
The syntax is:
#break [conditional_expression]
The conditional expression is optional and will default to the true
condition. Otherwise, the program will break out of the loop if the
conditional expression evaluates true, or the program will skip the
#break command if the conditional expression evaluates false.
Example:
#var a, b
#do a 1 ==
#break a 2 >
#print a=%a%
#eval a ++
#until
Would print:
a= 1
a= 2
The program initialized "a" to be 1 and then started a loop that
printed "a" and then incremented "a". The loop would have continued
forever, but the #break command broke out of the loop when "a" > 2.
The #sub, #endsub and #call commands:
The #sub and #endsub commands serve to define subprograms that may
be called by the #call command. In fact, a subprogram may be called
from within itself, allowing for recursive subprograms.
The command syntax is:
#sub subname
...
...
...
#endsub [optional_expression]
#call subname [conditional_expression]
The optional conditional expression after the #call command defaults
to the true condition. Otherwise the subprogram will be called if the
conditional expression evaluates true or the #call command will be
skipped if the conditional expression evaluates false.
Since the source file is parsed sequentially from beginning to end, I
suggest that you put your subprogram definitions at the beginning of
your source file so that they get defined before the first time they
are called. No commands within a subprogram definition will be
executed until the first time that subprogram is called from outside
that subprogram definition. This includes the optional expression
after the #endsub command which will not be evaluated while the
subprogram is being defined. It will be evaluated when the the
subprogram has been called and the end of the subprogram has been
reached.
Example:
#var a
#sub print_a
#eval a ++
a =%a%
#endsub
first call of print_a
#call print_a
returned from print_a
Would output:
first call of print_a
a = 1
returned from print_a
As P-cubed reads through the example, lines 3 and 4 are ignored at
first because they are inside a subprogram currently being defined.
Once P-cubed executes the #call command on line 7, then the next line
to be parsed will be line 3, the first line of the subprogram. Then the
rest of the lines in the subprogram will be parsed until the subprogram
reaches it's end and then returns to the line immediately following the
#call command that called it.
The last example may be modified to demonstrate recursion.
Example:
#var a
#sub print_a
#eval a ++
a =%a%
#call print_a a 2 <=
#endsub
first call of print_a
#call print_a
returned from print_a
Would output:
first call of print_a
a = 1
a = 2
a = 3
returned from print_a
The subprogram calls itself recursively until variable "a" equals 3
and then it returns back through the chain of recursive calls until it
has returned from the original call that started it all.
The #call command is also available as an RPN function.
Function: Stack Effect: Description:
!subname none Unconditional call of subprg "subname"
?subname (n1 -> ) Conditional call of subprg "subname"
If n1 is true (non-zero)
So the previous example with recursion could be written:
Example:
#var a
#sub print_a
#eval a ++
a =%a%
#eval a 2 <= ?print_a
#endsub
first call of print_a
#eval !print_a
returned from print_a
Would output:
first call of print_a
a = 1
a = 2
a = 3
returned from print_a
Variable definitions made within a called subprogram will be removed
when the called subprogram returns to it's caller.
Example:
#var a, b, c, d
#let a = 1
#let b = 2
#sub new_var
#var a, b
#let a = 3
#let b = 4
#print inside sub a =%a%, b =%b%
#endsub
#print before sub a =%a%, b =%b%
#call new_var
#print after sub a =%a%, b =%b%
Would output:
before sub a = 1, b = 2
inside sub a = 3, b = 4
after sub a = 1, b = 2
The subprogram makes new definitions of variables "a" and "b" and then
removes them when it returns to it's caller. The definitions of
variables "a" and "b" inside the subprogram could be considered local
variables in programmers terms, because they are only accessable within
the subprogram itself. The variables "c", and "d" could be considered
global variables because they are not defined within a subprogram and
are not redefined within any subprogram so they are always the same no
mattter where you are. When we are within the subprogram, the values
of "a" and "b" are set to 3, and 4, but we could access the old values
of "a" or "b" by using the reference [a:1] or [b:1].
This is not possible outside the subprogram because the multiple
definitions of variables "a" and "b" do not exist there.
The #digit command:
The #digit command directs P-cubed to truncate the integer part of all
numbers sent to the output file to a specified number of places before
the decimal point.
The syntax is:
#digit value
Example:
#digit 3
The example would truncate all numbers sent to the output to 3 places
before the decimal point. Note that this includes one place reserved
for the minus sign of a negative number. If the output has numbers
with % signs in front, that means that you did not allow enough places
for the integer part of that number and should increase the value
after the #digit command. The Default value is 7 places.
The #digit command is also an RPN function:
Value
Function: Stack:
digit (n1 -> )
So the command:
#eval 3 digit
Would perform the function as the example using the #digit command.
This is handy for changing output format in the middle of an output
line.
Example:(assuming a=1 and b=2000)
a=%(2 digit a)%, and b=%(5 digit b)%
Would output:
a= 1.00000, and b= 2000.00000
The #decim command:
The #decim command directs P-cubed to round off the fractional part
of all numbers sent to the output file to a specified number of digits
after the decimal point. The syntax is:
#decim value
Example:
#decim 6
The example would round off the fractional part of all numbers sent to
the output file to six places after the decimal point. The Default
value is 5 places.
The #decim command is also an RPN function:
Value
Function: Stack:
decim (n1 -> )
So the command:
#eval 3 decim
Would perform the function as the example using the #decim command.
This is handy for changing output format in the middle of an output
line.
Example:(assuming a=1.25 and b=2.00005)
a=%(2 decim a)%, and b=%(5 decim b)%
Would output:
a= 1.25, and b= 2.00005
The #seed command:
The #seed command directs P-cubed to seed the random number
generator for the "rand" and "noise" RPN functions.
The syntax is:
#seed [value]
Where the value is optional
Example:
#seed 1.2345
The value after the #seed command is used to seed the random number
generator. Processing a P-cubed file with the same random number seed
will produce the same sequence of random numbers each time. If the
value after the #seed command is omitted, then the random number
generator will be seeded by the internal timer, (the number of seconds
since midnight) and will produce a different sequence of random numbers
each time (assuming you haven't waited EXACTLY 24 hours to try again).
The #connect command:
The #connect command performs calculations for finding a cone
section that will smoothly connect two spheres of different radius.
This is useful for generating objects that are a series of connected
spheres (a.k.a. "connect-the-dots").
The syntax is:
#connect center1 radius1 center2 radius2
Where center1 and center2 are vector values that
are the centers of the spheres you are connecting and
radius1 and radius2 are floating point values that are
the radii of the spheres that you are connecting.
The cone parameters are left on the stack and may be assigned
to variables by:
#pop end1 radius1 end2 radius2
Example:
#connect {0, 0, 0} 4 {0, 0, 15} 10
#pop e1 r1 e2 r2
cone { %e1%, %r1%, %e2%, %r2% }
The example connects two spheres of radius 4 and 10. The parameters
for the cone section are left on the stack and are assigned to
variables by the #pop command. The POV cone statement would define a
cone section that would smoothly connect the two spheres.
An easier method would be to use the RPN connect function
Function: Stack:
connect (cent1 rad1 cent2 rad2 -> rad2 end2 rad1 end1)
Like the #connect command, the RPN connect function leaves the answers
on the stack, in the correct order so that you may pop them off the
top of the stack in the order:
#pop end1 radius1 end2 radius2
If you are going to put these values from the stack directly into a
raytracer cone statement, then you could use them directly from the
stack by doing:
cone { %()%, %()%, %()%, %()% }
Reading from left to right, the first "()" would pop end1 off of the
stack, the second "()" would pop radius1 off of the stack, the third
"()" would pop end2 off of the stack, and the last "()" would pop
radius2 off of the stack. This format is consistent with the POV and
Polyray cone statements.
The #read command:
The #read command will read one line of values out of an input file
that you have opened, and leave them on the stack, along with a count
of how many values that were found. This comes in handy for reading
in a file of raw numeric data such as raw triangle files (although it
will not perform any "smoothing" on raw triangle data). The #read
command only reads one line at a time. The numbers on this line may
be separated by spaces and/or commas. The numbers are pushed onto
the stack as they are read in from left to right, and when this is
done, a count of how many numbers were found is left on top of the
stack. This count will report 0 (zero) if the line was left blank.
This count will also report -1 when the end of the input file has been
reached. The file should contain numbers only, text will be
interpreted as variable names and may cause a "variable not found"
error. The syntax is:
#read variable
where "variable" contains the file number of a file
that you have opened for input
Example:
#var infile
#open in infile numbers.inp
#read infile
The example read one line of data from the file named "numbers.inp".
Suppose numbers.inp contained:
11, 12, 13, 14, 15
Then, after the #read command the stack would contain:
11 12 13 14 15 5
^Top of stack
The number on top of the stack indicates that the #read command found
five numbers on the line it read from the "numbers.inp" file.
Notice that they must be popped off of the top of stack in the reverse
order of the way they were read in. This is what is meant by the term
"last-in-first-out" stack.
Axis commands:
Axis commands affect an object I call a local axis. This is not to
be confused with the world axis which consists of the origin {0, 0, 0}
and the left handed coordinate system that has x, y, and z directions,
much the same as POVray and Polyray. Think of a local axis as a man
out in space. No matter what kind of crazy orientation we on earth
see him in, he has his own frame of reference that defines up, down,
right, left, forward, and back for him. The local axis starts out at
the origin {0, 0, 0} with it's own x, y and z vectors pointing along
the world's x, y, and z directions. From there we can tell it to turn
about it's own center and point it's x, y, and z vectors in different
directions. Then we can tell it to move in a direction relative to
it's own x, y, and z vectors. Once we have done several steps like
this, we can then find where the local axis ended up relative to
the origin {0, 0, 0} in the world's x, y, and z coordinates. It's kind
of like a remote control airplane that you can tell to pitch up and
down (rotate about it's own x vector), yaw right and left (rotate about
it's own y vector), and roll right or left (rotate about it's own z
vector). Then you can tell it to move so far left or right (along it's
own x vector), so far up or down (along it's own y vector), and so far
forward or back (along it's own z vector). When you are done, you can
find the absolute location in world coordinates of your remote control
airplane and place a raytracer object there (like skywriting).
L. J. Lapre's LPARSER uses commands like this to move a "turtle" (I
have yet to see a flying turtle), and do L-system objects like trees
and plants. With axis commands and recursive subprograms, I have been
able to do the same kind of L-system objects and output them directly
to raytracer format.
The #axis move command:
This command serves to move the local axis object along it's own x,
y, and z vectors. Remember the remote control airplane? Well, the
+x vector would point along the right wing, the +y vector would point
out of the top of the airplane, and the +z vector would point out of
the front of the airplane. The syntax is:
#axis move vector_value
Example:
#axis move {3, 4, 5}
The example would move the remote control airplane 3 units along the
direction of it's right wing, 4 units along the direction pointing out
of the top of the airplane and 5 units towards the front of the
airplane.
The #axis move command is also an RPN function:
Value Axis
Function: Stack: Stack:
move (n1 -> ) ( -> )
So the command:
#eval {3, 4, 5} move
Would perform the same move as the example using the #axis move command.
The #axis pitch command:
The axis turn command turns the local axis about it's own x vector in
order to point it's y, and z vectors in new directions. Pitch is when
you move the nose of the airplane up, or down by rotating about the axis
formed by your wings (in this case rotating about our +x vector pointing
along the right wing). Note that this has the effect of changing the
orientation of your airplane and pointing the y and z vectors in new
directions. The syntax is:
#axis pitch angle_value
Example:
#axis pitch 30
The example would Pitch the nose of the airplane down by 30 degrees
(rotating about the +x vector in a left hand coordinate system).
The #axis pitch command is also an RPN function:
Value Axis
Function: Stack: Stack:
pitch (n1 -> ) ( -> )
So the command:
#eval 30 pitch
Would do same function as the example using the #axis pitch command.
The #axis yaw command:
The axis yaw command turns the local axis about it's own y vector in
order to point it's x, and z vectors in new directions. Yaw is when you
move the nose of the airplane left or right by rotating about the axis
formed by the top an bottom of the airplane (in this case rotating about
the +y vector pointing out the top of our airplane). This will change
the airplane's orientation and will point the x, and z vectors in new
directions. The syntax is:
#axis yaw angle_value
Example:
#axis yaw 60
The example would Yaw the nose of the airplane right by 60 degrees
(rotating about the +y vector in a left hand coordinate system).
The #axis yaw command is also an RPN function:
Value Axis
Function: Stack: Stack:
yaw (n1 -> ) ( -> )
So the command:
#eval 60 yaw
Would do the same function as the example using the #axis yaw command.
The #axis roll command:
The #axis roll command rolls the local axis about it's own z vector
in order to point it's x and y vectors in new directions. Roll is when
you roll the airplane over by rotating about the axis formed by the nose
and tail of the airplane (in this case rotating about the +z vector
pointing out of the nose of the airplane). This points the airplanes x,
and y vectors in new directions. The syntax is:
#axis roll angle_value
Example:
#axis roll 90
The example would Roll the airplane left by 90 degrees (rotating
about the +z vector in a left handed coordinate system).
The #axis roll command is also an RPN function:
Value Axis
Function: Stack: Stack:
roll (n1 -> ) ( -> )
So the command:
#eval 90 roll
Would do the same function as the example using the #axis roll command.
The #axis rotate command:
The #axis rotate command rotates the local axis about it's center
relative to the worlds x, y, and z directions. This will be a left
handed rotation. That is, point your left thumb in the direction of the
vector you wish to turn the airplane about, and your fingers curl in the
direction of the turn. Think of the airplane as a raytracer object in
POV or Polyray and you are changing it's orientation with the "rotate"
object modifier. The syntax is:
#axis rotate vector_value
Example:
#axis rotate {10, 20, 30}
The example would rotate the airplane about it's center by 10 degrees
about the world's x direction, then by 20 degrees about the world's y
direction, and then by 30 degrees about the world's z direction.
The #axis rotate command is also an RPN function:
Value Axis
Function: Stack: Stack:
arot (n1 -> ) ( -> )
So the command:
#eval {30, 60, 90} arot
Would perform the same as the example using the #axis rotate command.
The #axis turn command:
The axis turn command turns the local axis about an arbitrary absolute
vector in order to point it's x, y, and z vectors in new directions.
To the remote control airplane, this would be equivalent to you reaching
out and turning the airplane about an axis of your choosing. This
will be a left handed rotation. That is, point your left thumb in the
direction of the vector you wish to turn the airplane about, and your
fingers curl in the direction of the turn. The airplanes location is
not changed, but the x, y, and z vectors will point in different
directions. The syntax is:
#axis turn vector_value angle_value
Example:
#axis turn {1, 1, 1} 45
The example would turn the airplane about the vector {1, 1, 1} by 45
degrees. Imagine connecting an end of a stick that points in the absolute
direction {1, 1, 1} to your remote control airplane and then rolling
that stick between your fingers so that the airplane connected to the
end turns 45 degrees about the axis of the stick.
The #axis turn command is also an RPN function:
Value Axis
Function: Stack: Stack:
aturn (n1 n2 -> ) ( -> )
So the command:
#eval {1, 1, 1} 45 aturn
Would perform the same function as the example using the #axis turn command.
The #axis location? command:
The #axis location? command will get the current location of the
local axis in absolute world coordinates. In other words, after you
have told the remote control airplane to to pitch, yaw, roll, and move
several times, this command will report the location of the airplane
relative to the origin {0, 0, 0} and the world's x, y, z directions.
The syntax is:
#axis location? variable
Example:
#axis location? a
The example would get the current location (as a vector) of the remote
control airplane and assign it to variable "a". As I said, you could
then have a raytracer statement put an object at this location and the
effect would be as if you flew your remote control airplane to a
certain point in the air and had it hang a raytracer object there.
The #axis location? command is also an RPN function:
Value Axis
Function: Stack: Stack:
loc? ( -> n1) ( -> )
So the command:
#eval a loc? ==
Would get the same answer as the example using the #axis location? command.
The #axis push command:
The #axis push command will take the local axis location and
orientation and push them on to a "last in first out" stack. This is
like taking a snapshot of the remote control airplane's location and
orientation and putting it in top of a pile so that you can look at it
later and put the airplane back the way it was when the snapshot was
taken. The syntax is:
#axis push
The #axis push command is also an RPN function:
Value Axis
Function: Stack: Stack:
apush ( -> ) ( -> ax1)
So the command:
#eval apush
Would perform the same function as the example using the #axis push command.
The #axis pop command:
The #axis pop command will set the local axis location and
orientation to be the same as values it pops off of the top of a "last
in first out" stack. This is like picking up a snapshot of the remote
control airplane's location and orientation off of the top of a pile
and using it to put the airplane back the way it was when the snapshot
was taken. The syntax is:
#axis pop
The #axis pop command is also an RPN function:
Value Axis
Function: Stack: Stack:
apop ( -> ) (ax1 -> )
So the command:
#eval apop
Would perform the same function as the example using the #axis pop command.
The #axis dupe command:
The #axis dupe command will make a copy of the local axis location and
orientation it finds on top of the "last in first out" stack and will put
the copy on top of the stack. This is like making a copy of a snapshot
of the remote control airplane's location and orientation from the snapshot
on top of the pile and then putting the copy on top of the pile.
The syntax is:
#axis dupe
The #axis dupe command is also an RPN function:
Value Axis
Function: Stack: Stack:
adupe ( -> ) (ax1 -> ax1 ax1)
So the command:
#eval adupe
Would perform the same function as the example using the #axis dupe command.
The #axis swap command:
The #axis swap command will swap the top 2 copies of the local axis
location and orientation it finds on top of the "last in first out"
stack. This is like swapping the top 2 snapshots of the remote control
airplane's location and orientation that are on top of the pile.
The syntax is:
#axis swap
The #axis swap command is also an RPN function:
Value Axis
Function: Stack: Stack:
aswap ( -> ) (ax1 ax2 -> ax2 ax1)
So the command:
#eval aswap
Would perform the same function as the example using the #axis swap command.
The #axis drop command:
The #axis drop command will destroy the top copy of the local axis
location and orientation it finds on top of the "last in first out"
stack. This is like tossing out the top snapshot of the remote control
airplane's location and orientation that was on top of the pile.
The syntax is:
#axis drop
The #axis swap command is also an RPN function:
Value Axis
Function: Stack: Stack:
adrop ( -> ) (ax1 -> )
So the command:
#eval adrop
Would perform the same function as the example using the #axis drop command.
The #axis drag command:
The #axis drag command will take the local axis object and drag it
in a specified direction relative to the world's x, y, and z
directions. The effect is as if you had whipped up a gust of wind and
dragged the remote control airplane from it's current location by a
certain amount and didn't change the airplane's orientation at all.
The syntax is:
#axis drag vector_value
Example:
#axis drag {3, 4, 5}
The example would take the airplane and drag it 3 units in the
world's x direction, 4 units in the world's y direction, and 5 units
in the world's z direction.
The #axis drag command is also an RPN function:
Value Axis
Function: Stack: Stack:
drag (n1 -> ) ( -> )
So the command:
#eval {3, 4, 5} drag
Would perform the same function as the example using the #axis drag command.
The #axis place command:
The #axis place command takes the local axis object and places it at
an x, y, and z world coordinate relative to the origin. The effect
is as if you suddenly teleported the remote control airplane from
wherever it is to a certian point in the air specified by world x, y,
and z coordinates. The airplane's orientation will not be changed at
all. The syntax is:
#axis place vector_value
Example:
#axis place {0, 0, 0}
The example will get the airplane from wherever it is and place it at
the origin. It will now report a location of <0, 0, 0>.
The #axis place command is also an RPN function:
Value Axis
Function: Stack: Stack:
place (n1 -> ) ( -> )
So the command:
#eval {0, 0, 0} place
Would perform the same move as the example using the #axis place command.
The #axis orient command:
The axis orient command will rotate the local axis object so that
it's x, y, and z vectors point along the world's x, y and z directions.
The effect is as if you took the remote control airplane, put it straight
and level. The airplane's location does not change.
The syntax is:
#axis orient
Example:
#axis orient
The example would flop the airplane over and turn it so that it's
right wing (+x vector) pointed towards the world's +x direction, and
the top of the airplane (+y vector) pointed in the world's +y
direction, and the nose of the airplane (+z vector) points toward the
world's +z direction.
The #axis orient command is also an RPN function:
Value Axis
Function: Stack: Stack:
orient ( -> ) ( -> )
So the command:
#eval orient
Would perform the same move as the example using the #axis orient command.
The #axis rotation? command:
The #axis rotation? command will report the current orientation of
the local axis object relative to rotation about the world's x, y,
and z directions. Think of it as asking "If I had another airplane,
sitting straight and level, How much would I have to rotate it about
the world's x, y, and z directions in order to put it in the same
orientation as the current airplane?".
The syntax is:
#axis rotation? variable
Example:
#axis rotation? a
The example would find the current orientation of the local axis and put
it (as a vector) in the variable "a". This can be useful if you have
a raytracer object that you want to rotate so that it aligns with the
local axis. You might even define a raytraced airplane object and put
it in the same orientation as the local axis, which I compare to a
remote control airplane so much.
The #axis rotation? command is also an RPN function:
Value Axis
Function: Stack: Stack:
rot? ( -> n1) ( -> )
So the command:
#eval a rot? ==
Would find the same value as the example using the #axis rotation? command.
The #axis roll? command:
The #axis roll? command will will report the roll angle of the local
axis object. This is like finding out how far the airplane has rolled
about it's +z vector from "wings level" (the +x vector being level
with the "ground" and the +y vector pointing skyward).
The syntax is:
#axis roll? variable
Example:
#axis roll? a
The example would find out how far the airplane has rolled from "wings
level", and put it as a number in variable "a". To make the airplane
level it's wings we could use the command "#axis turn {0, 0, -a}".
We must roll by -a because the airplane is already at the roll angle
in variable "a" and we must set it back to zero.
The #axis roll? command is also an RPN function:
Value Axis
Function: Stack: Stack:
roll? ( -> n1) ( -> )
So the command:
#eval a roll? ==
Would find the same value as the example using the #axis roll? command.
The #axis yaw? command:
The #axis yaw? command will report the yaw angle of the local axis
object. This is like finding out how far the airplane has rotated
about it's +y vector from having it's nose pointed only in the world's
y and z directions and it's right hand wing on the world's +x side of
the airplane.
The syntax is:
#axis yaw? variable
Example:
#axis yaw? a
The #axis yaw? command is also an RPN function:
Value Axis
Function: Stack: Stack:
yaw? ( -> n1) ( -> )
So the command:
#eval a yaw? ==
Would find the same value as the example using the #axis yaw? command.
The #axis pitch? command:
The #axis pitch? command will report the pitch angle of the local
axis object. This is like finding out how far the airplane has
rotated about it's wings (the +x vector) from having it's nose level
with the ground and it's +y vector pointing skyward.
The syntax is:
#axis pitch? variable
Example:
#axis pitch? a
The #axis pitch? command is also an RPN function:
Value Axis
Function: Stack: Stack:
pitch? ( -> n1) ( -> )
So the command:
#eval a pitch? ==
Would find the same value as the example using the #axis pitch? command.
The #axis swing command:
The #axis rotate command will take the local axis object and rotate
it about the origin and the world's x, y, and z directions. This is
like standing at the origin and throwing a lasso around your remote
control airplane, and then swinging the airplane around the origin
relative to the worlds x, y, and z directions. The syntax is:
#axis swing vector_value
Example:
#axis swing {30, 40, 50}
The example will take the airplane and rotate it about the world's x,
y, and z directions by 30, 40, and 50 degrees respectively. If the
airplane was not at located at the origin, then it's location would be
rotated about the origin as well as having it's orientation rotated.
The #axis swing command is also an RPN function:
Value Axis
Function: Stack: Stack:
swing (n1 -> ) ( -> )
So the command:
#eval {30, 40, 50} swing
Would perform the same move as the example using the #axis swing command.
The #axis sweep commmand:
The #axis sweep command will sweep the local axis around an arbitrary
vector. This is like going out to a point in space, throwing a lasso
around the remote control airplane, and swinging the airplane around an
axis of your choosing. This will be a left handed rotation. That is,
point your left thumb in the direction of the vector you intend to sweep
the local axis about and your fingers will point in the direction of the
rotation. The syntax is:
#axis sweep axis_vector angle_value
Example:
#axis sweep {1, 1, 1} 45
The Example would be like you starting at the origin, moving along the
vector {1, 1, 1} to the point on that vector closest to the airplane, and
then throwing your lasso around the airplane and swinging the airplane
around the vector {1, 1, 1} by 45 degrees. The airplane's location will
be rotated around the vector as well as the orientation.
The #axis sweep command is also an RPN function:
Value Axis
Function: Stack: Stack:
sweep (n1 n2 -> ) ( -> )
So the command:
#eval {1, 1, 1} 45 sweep
Would perform the same move as the example using the #axis sweep command.
The #axis bend command:
The #axis bend command is used to adjust the orientation of the
local axis object in order to simulate bending due to gravity as the
local axis moves from point to point. This is like when the remote
control airplane moves in a certain direction, but gravity in the
axis "down" direction pulls on it so that it ends up a little lower then
it would have been if there were no gravity. The axis "down"
direction defaults to the -y world direction, but may be changed by
the #axis down command (see below). You specify the direction
that you intend to move in, and the amount of gravity in the axis down
direction, and the command will change the orientation of the airplane
so that a move in that direction (relative to the airplane's x, y, and z
vectors) will end up a little lower in world coordinates then it would
have been with no gravity. The syntax is:
#axis bend vector_value gravity_value
Example:
#axis bend {0, 0, 10} 0.2
#axis move {0, 0, 10}
In the example, assuming we started out straight and level at the
origin, instead of ending up at the location {0, 0, 10}, gravity would
pull down on this move in the -y world direction so that the airplane
would end up at approximate location {0, -2, 9.8}. The #axis bend
command has actually pitched the nose of the airplane down a little so
that a move of {0, 0, 10} ends up at location {0, -2, 9.5}. Note that
gravity would have no effect if we were trying to move straight up (+y
world direction) or straight down (-y world direction), the same way
that gravity would not bend a tree branch pointing straight up or
straight down. The tree branch that gravity would bend the most is
one hanging out over the ground. Funny I should mention tree branches
because this command can be used to simulate the "tropism" (bending of
plant branches due to gravity) command used in the L-systems included
with L. J. Lapre's LPARSER. Some example files of trees taken from
LPARSER (which LPARSER has taken from the Book "The Algorithmic Beauty
of Plants"), are included along with this package.
The #axis bend command is also an RPN function:
Value Axis
Function: Stack: Stack:
bend (n1 n2 -> ) ( -> )
So the command:
#eval {0, 0, 10} dupe 0.2 bend move
Would perform the same bend and move as the example using the #axis
bend and #axis move commands.
The #axis down command:
The #axis down command sets the "down" direction for the #axis bend
command. This is like setting which way that gravity pulls on the
remote control airplane. The syntax is:
#axis down vector_value
Example:
#axis down {1, 0, 0}
#axis bend {0, 0, 10} 0.2
#axis move {0, 0, 10}
The example sets the down direction to be in the +x world direction
and then performs a bend and move in the {0, 0, 10} direction. The
net effect is that gravity now pulls on this move in the +x world
direction so that this move would put the axis at {2, 0, 9.8} in
absolute world coordinates instead of {0, 0, 10} if there were no
bend.
The #axis down command is also an RPN function:
Value Axis
Function: Stack: Stack:
adown (n1 -> ) ( -> )
So the command:
#eval {1, 0, 0} adown {0, 0, 10} dupe 0.2 bend move
Would perform the same bend and move as the example using the #axis
down, #axis bend and #axis move commands.
The #axis relcoord? command
The axis relcoord? command will take a coordinate in absolute x, y,
and z directions from the origin and find the relative coordinate
along the local axis x, y, and z vectors from the center of the local
axis. That is like asking the remote control airplane how far away
a certain point in space is from the airplane.
The syntax is:
#axis relcoord? variable vector_value
Example:
#axis relcoord? a {10, 10, 10}
The Example will set "a" equal to a vector value that represents the
distance along the airplanes x, y, and z vectors that the absolute
point {10, 10, 10} is away from the airplane.
The #axis relcoord? command is also an RPN function:
Value Axis
Function: Stack: Stack:
relcoord? (n1 -> n2) ( -> )
So the command:
#eval a {10, 10, 10} relcoord? ==
Would perform the same function as the example using the #axis relcoord?
command.
The #axis abscoord? command
The axis abscoord? command will take a coordinate in distances along
the local axis x, y, and z vectors from the center of the local axis and
find the absolute coordinate along the world x, y, and z directions from
the origin. That is like asking the remote control airplane what the
world coordinate is of a point that is a certain distance from it.
The syntax is:
#axis abscoord? variable vector_value
Example:
#axis abscoord? a {10, 10, 10}
The Example will set "a" equal to a vector value that represents the
distance along the world's x, y, and z directions from the origin of a
point that is {10, 10, 10} in distances along the airplanes x, y, and
z vectors away from the airplane.
The #axis abscoord? command is also an RPN function:
Value Axis
Function: Stack: Stack:
abscoord? (n1 -> n2) ( -> )
So the command:
#eval a {10, 10, 10} abscoord? ==
Would perform the same function as the example using the #axis abscoord?
command.
The #axis alignx command:
The #axis alignx command is used to aim the local axis x vector at a
certain point specified in absolute world coordinates. This like
telling the remote control airplane to point it's right wing at a
certain point in space.
The syntax is:
#axis alignx vector_value
Example:
#axis alignx {10, 10, 10}
The example would aim the right wing of the airplane at the point
{10, 10, 10} (specified in distances along the world's x, y, and z
directions from the origin). The airplane will accomplish by first
doing a yaw about it's y vector and then doing a roll about it's z
vector. So the x vector will be aiming at the specified point and the
y and z vectors will be pointing in new directions also.
The #axis alignx command is also an RPN function:
Value Axis
Function: Stack: Stack:
alignx (n1 -> ) ( -> )
So the command:
#eval {10, 10, 10} alignx
Would perform the same function as the example using the #axis alignx
command.
The #axis aligny command:
The #axis aligny command is used to aim the local axis y vector at a
certain point specified in absolute world coordinates. This like
telling the remote control airplane to point it's top at a certain point
in space.
The syntax is:
#axis aligny vector_value
Example:
#axis aligny {10, 10, 10}
The example would aim the top of the airplane at the point {10, 10, 10}
(specified in distances along the world's x, y, and z directions from
the origin). The airplane will accomplish by first doing a pitch about
it's x vector and then doing a roll about it's z vector. So the y vector
will be aiming at the specified point and the x and z vectors will be
pointing in new directions also.
The #axis aligny command is also an RPN function:
Value Axis
Function: Stack: Stack:
aligny (n1 -> ) ( -> )
So the command:
#eval {10, 10, 10} aligny
Would perform the same function as the example using the #axis aligny
command.
The #axis alignz command:
The #axis alignz command is used to aim the local axis z vector at a
certain point specified in absolute world coordinates. This like
telling the remote control airplane to point it's nose at a certain
point in space.
The syntax is:
#axis alignz vector_value
Example:
#axis alignz {10, 10, 10}
The example would aim the nose of the airplane at the point {10, 10, 10}
(specified in distances along the world's x, y, and z directions from
the origin). The airplane will accomplish by first doing a pitch about
it's x vector and then doing a yaw about it's y vector. So the z vector
will be aiming at the specified point and the x and y vectors will be
pointing in new directions also.
The #axis alignz command is also an RPN function:
Value Axis
Function: Stack: Stack:
alignz (n1 -> ) ( -> )
So the command:
#eval {10, 10, 10} alignz
Would perform the same function as the example using the #axis alignz
command.
Tips:
LPARSER's "$" command (roll the "turtle" until it is level) is
equivalent to the axis commands:
#axis roll? r
#axis roll -r
or the RPN expression:
#eval -(roll?) roll
Don't try to split subprogram definitions, If blocks, or
DO-UNTIL loops between input files. Meaning don't have the #sub
command in one input file and the #endsub command in another, or the
#do and #until commands in different files, or the #if, #else, and #endif
commands in separate input files.
If you define subprograms in a separate input file, do not close
that file until you are all done calling those subprograms.
Failing to put the #seed command in a program which uses random
numbers has the same effect as seeding the random number generator
with the same value every time, and you will get the same sequence of
random numbers each time.
Disclaimer:
This program is guaranteed to occupy disk space.
This program is guaranteed to be originally written by John R. Wind.
Beyond that, you are on your own.